package com.wall.haven.exception.handler;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.api.IErrorCode;
import com.baomidou.mybatisplus.extension.api.R;
import com.wall.haven.enums.ResultCode;
import com.wall.haven.exception.ServiceException;
import com.wall.haven.util.RequestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;

/**
 * 默认统一异常处理
 *
 * @author ChenWangKun
 * @date 2023-05-08
 **/
@Order(value = Ordered.LOWEST_PRECEDENCE)
@RestControllerAdvice
public class DefaultExceptionAdvice {

    /**
     * logger
     */
    private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionAdvice.class);

    /**
     * 返回状态码:405
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public R handleHttpRequestMethodNotSupportedException(HttpServletRequest req, HttpRequestMethodNotSupportedException e) {
        return defHandler("不支持当前请求方法", e, req);
    }

    /**
     * 返回状态码:415
     */
    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    public R handleHttpMediaTypeNotSupportedException(HttpServletRequest req, HttpMediaTypeNotSupportedException e) {
        return defHandler("不支持当前媒体类型", e, req);
    }

    /**
     * 捕获参数转化异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public R messageConvertException(HttpServletRequest req, HttpMessageNotReadableException e) {
        return defHandler("参数转化失败", e, req);
    }

    /**
     * 捕获缺少参数异常
     */
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public R missingServletRequestParameterException(HttpServletRequest req, MissingServletRequestParameterException e) {
        return defHandler(e.getMessage(), e, req);
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public R argumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return defHandler(message, e, req);
    }

    /**
     * 捕获上传文件大小超过限制异常
     */
    @ExceptionHandler({MaxUploadSizeExceededException.class})
    public R maxUploadSizeExceededException(HttpServletRequest req, MaxUploadSizeExceededException e) {
        return defHandler("上传文件大小超过限制", e, req);
    }


    /**
     * SQLException sql异常处理
     */
    @ExceptionHandler({SQLException.class})
    public R handleSQLException(HttpServletRequest req, SQLException e) {
        return defHandler("服务运行SQLException异常", e, req);
    }

    /**
     * ServiceException 业务异常处理
     *
     */
    @ExceptionHandler(ServiceException.class)
    public R handleServiceException(HttpServletRequest req, ServiceException e) {
        return defHandler(e.getMessage(), e, req);
    }

    /**
     * 所有异常统一处理
     * 返回状态码:500
     */
    @ExceptionHandler(Exception.class)
    public R handleException(HttpServletRequest req, Exception e) {
        return defHandler("服务异常！", e, req);
    }

    /**
     * 处理异常
     *
     * @author ChenWangKun
     * @date 2023/5/8
     */
    public static R defHandler(ResultCode resultCode, Exception e, HttpServletRequest req) {
        printRequestLog(req);
        LOG.error(resultCode.msg, e);
        if (e instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) e;
            return handlerServiceException(serviceException);
        }
        return R.failed(resultCode);
    }

    public static R defHandler(String msg, Exception e, HttpServletRequest req) {
        printRequestLog(req);
        LOG.error(msg, e);
        if (e instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) e;
            return handlerServiceException(serviceException);
        }
        return R.failed(msg);
    }

    private static R handlerServiceException(ServiceException e){
        return R.failed(new IErrorCode() {
            @Override
            public long getCode() {
                return e.getCode();
            }
            @Override
            public String getMsg() {
                return e.getMessage();
            }
        });
    }

    /**
     * 打印请求信息
     *
     * @param req
     * @author ChenWangKun
     * @date 2023/5/8
     */
    public static void printRequestLog(HttpServletRequest req) {
        try {
            String requestBody = RequestUtil.getRequestBody(req);
            String requestBodyStr = "";
            if (StringUtils.isNotBlank(requestBody)) {
                requestBodyStr = String.format(", body=\"%s\"", requestBody);
            }
            Map<String, String[]> parameterMap = req.getParameterMap();
            LOG.error("Request Info: {uri={}, method={}, params={}{}}",
                    req.getRequestURI(),
                    req.getMethod(),
                    parameterMap,
                    requestBodyStr);
        } catch (IOException e) {
            LOG.error("打印请求参数失败！", e);
        }
    }
}